package com.ays.mesbackbone.common.utils;

import lombok.var;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.context.expression.BeanFactoryResolver;
import org.springframework.context.expression.MethodBasedEvaluationContext;
import org.springframework.core.LocalVariableTableParameterNameDiscoverer;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;

import java.lang.reflect.Method;
import java.util.Objects;

/**
 * <p>Title： SpelUtil </p>
 * <p>Description：SpelUtil </p>
 *
 * @author sujunxuan
 * @version V1.0
 * @date 2019/7/29 15:38
 */
@SuppressWarnings("all")
public class SpelUtil {

    private static BeanFactory beanFactory;

    public static void setBeanFactory(BeanFactory beanFactory) {
        SpelUtil.beanFactory = beanFactory;
    }

    public static <T> T parse(String spel, Method method, Object[] args, Class<T> clazz) {

        //获取被拦截方法参数名列表(使用Spring支持类库)
        var u = new LocalVariableTableParameterNameDiscoverer();
        var paraNameArr = u.getParameterNames(method);

        //使用SPEL进行key的解析
        var parser = new SpelExpressionParser();

        //SPEL上下文
        var context = new StandardEvaluationContext();
        context.setBeanResolver(new BeanFactoryResolver(SpelUtil.beanFactory));

        //把方法参数放入SPEL上下文中
        for (int i = 0; i < Objects.requireNonNull(paraNameArr).length; i++) {
            context.setVariable(paraNameArr[i], args[i]);
        }
        return parser.parseExpression(spel).getValue(context, clazz);
    }

    /**
     * 支持 #p0 参数索引的表达式解析
     *
     * @param rootObject 根对象,method 所在的对象
     * @param spel       表达式
     * @param method     目标方法
     * @param args       方法入参
     * @return 解析后的字符串
     */
    public static <T> T parse(Object rootObject, String spel, Method method, Object[] args, Class<T> clazz) {

        //获取被拦截方法参数名列表(使用Spring支持类库)
        var u = new LocalVariableTableParameterNameDiscoverer();
        var paraNameArr = u.getParameterNames(method);

        //使用SPEL进行key的解析
        var parser = new SpelExpressionParser();

        //SPEL上下文
        var context = new MethodBasedEvaluationContext(rootObject, method, args, u);

        //把方法参数放入SPEL上下文中
        for (int i = 0; i < Objects.requireNonNull(paraNameArr).length; i++) {
            context.setVariable(paraNameArr[i], args[i]);
        }
        return parser.parseExpression(spel).getValue(context, clazz);
    }
}
